
#include <stdio.h>
#include <BAudioOutput>
#include "member_BAudioOutput.h"
#include "IGlobal.h"

using namespace BWE;

#define member					(*(member_BAudioOutput*)_ptr)
#define member_allocate()		_ptr = new member_BAudioOutput(this)
#define member_release()		delete (member_BAudioOutput*)_ptr

BList<BAudioOutput*>	AudioOutputList;

BAudioOutput::BAudioOutput()
{
	member_allocate();
	AudioOutputList.append(this);
}
BAudioOutput::~BAudioOutput()
{
	AudioOutputList.remove(this);
	member_release();
}

bool BAudioOutput::setFormat(Format format)
{
	if (member.format != format)
	{
		member.format = format;
		member.dirtyFormat = true;
	}
	return true;
}
Format BAudioOutput::format() const
{
	return member.format;
}

bool BAudioOutput::setChannelCount(int channelCount)
{
	if (member.channelCount != channelCount)
	{
		member.channelCount = channelCount;
		member.dirtyFormat = true;
	}
	return true;
}
int BAudioOutput::channelCount() const
{
	return member.channelCount;
}

bool BAudioOutput::setSampleRate(int sampleRate)
{
	if (member.sampleRate != sampleRate)
	{
		member.sampleRate = sampleRate;
		member.dirtyFormat = true;
	}
	return true;
}
int BAudioOutput::sampleRate() const
{
	return member.sampleRate;
}

#ifdef linux

int BAudioOutput::DeviceNames(BStringList& namelst)
{
	namelst.clear();
	namelst.append("/dev/dsp");
	return namelst.size();
}

bool BAudioOutput::open()
{
	char *pathname = 0;
	int nSampleRate = 0;
	int nChannels = 2;
	int fmt = 0;

	int mix_fd,status,arg;
	member.dsp_fd = ::open("/dev/dsp" , O_RDWR);   /*open dsp*/
	if(member.dsp_fd < 0)
	{
		puts("open dsp failed!");
		return false;
	}
	arg = nSampleRate;
	status = ioctl(member.dsp_fd,SOUND_PCM_WRITE_RATE,&arg); /*set samplerate*/
	if(status < 0)
	{
		::close(member.dsp_fd);
		puts("samplerate status failed");
		return false;
	} 
	if(arg != nSampleRate)
	{
		::close(member.dsp_fd);
		puts("set samplerate failed");
		return false;
	} 
	arg = nChannels;  /*set channels*/   
	status = ioctl(member.dsp_fd, SOUND_PCM_WRITE_CHANNELS, &arg); 
	if(status < 0)
	{
		::close(member.dsp_fd);
		puts("Channels status failed");
		return false;
	} 
	if( arg != nChannels)
	{
		::close(member.dsp_fd);
		puts("set channels failed");
		return false;
	}
	arg = fmt; /*set bit fmt*/
	status = ioctl(member.dsp_fd, SOUND_PCM_WRITE_BITS, &arg); 
	if(status < 0)
	{
		::close(member.dsp_fd);
		puts("FMT status failed");
		return false;
	} 
	if(arg != fmt)
	{
		::close(member.dsp_fd);
		puts("set fmt failed");
		return false;
	}
	member.file_fd = fopen(pathname,"r");
	if(member.file_fd == NULL)
	{
		::close(member.dsp_fd);
		puts("opem filed failed");
		return false;
	}
	return true;
}
bool BAudioOutput::close()
{
	if(member.dsp_fd)
	{
		::close(member.dsp_fd);
		fclose(member.file_fd);
		member.dsp_fd = 0;
		member.file_fd = 0;
		return true;
	}
	return false;
}

bool BAudioOutput::start()
{
	if (member.dirtyFormat)
	{
		member.initFormat();
	}
	int num = 3 * member.channelCount * member.sampleRate * member.fmtlen / 8;
	int get_num; 
	char buf[num];
	while(feof(member.file_fd) == 0)
	{
		get_num = fread(buf, 1, num, member.file_fd);
		write(member.dsp_fd, buf, get_num); 
		if(get_num != num)
		{
			::close(member.dsp_fd);
			fclose(member.file_fd);
			member.dsp_fd = 0;
			member.file_fd = 0;
			return 0;
		}
	}
	return true;
}
bool BAudioOutput::pause()
{
	 return true;
}
bool BAudioOutput::stop()
{
	 return true;
}

void BAudioOutput::setBuffer(const BByteArray& data)
{
	member.buffer = data;
}
const BByteArray& BAudioOutput::buffer() const
{
	return member.buffer;
}

#endif

#ifdef _WIN32
void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
	for(BList<BAudioOutput*>::iterator it = AudioOutputList.begin(); it != AudioOutputList.end(); it++)
	{
		BAudioOutput* audio = it.value();
		member_BAudioOutput* mem = audiooutput_member(audio);
		if(mem->hWaveOut != hWaveOut)
		{
			return;
		}
		switch (uMsg)
		{
		case WOM_OPEN:
			break;
		case WOM_DONE:
		{
			LPWAVEHDR wavHdr = (LPWAVEHDR)dwParam1;
			int length = wavHdr->dwBufferLength;
			char* data = wavHdr->lpData;
			EnterCriticalSection(&mem->bufferSection);
			printf("audio output %d.\n", length);
			LeaveCriticalSection(&mem->bufferSection);
			break;
		}
		case WOM_CLOSE:
			break;
		}
	}
}

int BAudioOutput::DeviceNames(BStringList& namelst)
{
	namelst.clear();
	int num = waveOutGetNumDevs();
	for (int i = 0; i < num; i++)
	{
		WAVEOUTCAPS woc;
		waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS));
		namelst.append(woc.szPname);
	}
	return namelst.size();
}

bool BAudioOutput::open()
{
	member.initFormat();
	MMRESULT result = waveOutOpen(&member.hWaveOut, WAVE_MAPPER, &member.wavform, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION);
	switch(result)
	{
	case MMSYSERR_NOERROR:
		member.state = BAudio::State_Opened;
		return true;
	case MMSYSERR_ALLOCATED:
		puts("Specified resource is already allocated.");
		break;
	case MMSYSERR_BADDEVICEID:
		puts("Specified device identifier is out of range.");
		break;
	case MMSYSERR_NODRIVER:
		puts("No device driver is present.");
		break;
	case MMSYSERR_NOMEM:
		puts("Unabled to allocate or lock memory.");
		break;
	case WAVERR_BADFORMAT:
		puts("Attempted to open with an unsupported waveform-audio format.");
		break;
	case WAVERR_SYNC:
		puts("The device is synchronous but waveOutOpen was called without using the WAVE_ALLOWSYNC flag.");
		break;
	}
	WAVEOUTCAPS woc;
	waveOutGetDevCaps((UINT_PTR)member.hWaveOut, &woc, sizeof(WAVEOUTCAPS));  
	return false;
}
bool BAudioOutput::close()
{
	waveOutReset(member.hWaveOut);
	waveOutUnprepareHeader(member.hWaveOut, &member.wavhdr, sizeof(WAVEHDR));
	waveOutClose(member.hWaveOut);
	return true;
}

bool BAudioOutput::start()
{
	if (member.dirtyFormat)
	{
		member.initFormat();
		if (member.state = BAudio::State_Opened)
		{
			waveOutClose(member.hWaveOut);
			member.state = BAudio::State_Closed;
		}
		MMRESULT result = waveOutOpen(&member.hWaveOut, WAVE_MAPPER, &member.wavform, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION);
		switch (result)
		{
		case MMSYSERR_NOERROR:
			member.state = BAudio::State_Opened;
			break;
		case MMSYSERR_ALLOCATED:
			puts("Specified resource is already allocated.");
			break;
		case MMSYSERR_BADDEVICEID:
			puts("Specified device identifier is out of range.");
			break;
		case MMSYSERR_NODRIVER:
			puts("No device driver is present.");
			break;
		case MMSYSERR_NOMEM:
			puts("Unabled to allocate or lock memory.");
			break;
		case WAVERR_BADFORMAT:
			puts("Attempted to open with an unsupported waveform-audio format.");
			break;
		case WAVERR_SYNC:
			puts("The device is synchronous but waveOutOpen was called without using the WAVE_ALLOWSYNC flag.");
			break;
		}
	}
	waveOutPrepareHeader(member.hWaveOut, &member.wavhdr, sizeof(WAVEHDR));
	waveOutWrite(member.hWaveOut, &member.wavhdr, sizeof(WAVEHDR));
	return true;
}
bool BAudioOutput::pause()
{
	 return true;
}
bool BAudioOutput::stop()
{
	 return true;
}

void BAudioOutput::setBuffer(const BByteArray& data)
{
	member.buffer = data;
	memset(&member.wavhdr, 0, sizeof(WAVEHDR));
	member.wavhdr.lpData = (LPSTR)data.data();
	member.wavhdr.dwBufferLength = data.size();
	member.wavhdr.dwLoops = 1;
}
const BByteArray& BAudioOutput::buffer() const
{
	return member.buffer;
}

#endif